package com.common.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.common.dto.ArticleDTO;
import com.common.exception.ModelCoverDTOFailedException;
import com.common.exception.ModelNotFoundException;
import com.common.mapper.ArticleMapper;
import com.common.model.Article;
import com.common.query.ArticleQueryParam;
import com.common.service.ArticleService;
import com.common.service.CommentService;
import com.common.service.TagsService;
import com.util.CombineBeansUtil;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@CacheConfig(cacheNames = "article")
public class ArticleServiceImpl implements ArticleService {

    private ArticleMapper articleMapper;

    @Resource
    private CommentService commentService;

    @Resource
    private TagsService tagsService;

    public ArticleServiceImpl(ArticleMapper articleMapper) {
        this.articleMapper = articleMapper;
    }

    @Override
    public List<ArticleDTO> findAll(ArticleQueryParam articleQueryParam) {

        if (articleQueryParam.getOrder() == null) {
            articleQueryParam.setOrder("article_id");
        }

        if (articleQueryParam.getSort() == null) {
            articleQueryParam.setSort("desc");
        }

        List<Article> articles = articleMapper.selectAllWithUser(articleQueryParam);

        if (articles != null) {
            return coverToDTOList(articles);
        } else {
            return null;
        }
    }

    @Override
    public List<ArticleDTO> findHotArticlesWithUser(int size) {

        ArticleQueryParam articleQueryParam = new ArticleQueryParam();
        articleQueryParam.setOrder("article_id");
        articleQueryParam.setSort("desc");

        List<ArticleDTO> articles = this.findAll(articleQueryParam);
        //根据文章hot值排序 然后返回
        articles = articles.stream().sorted((a1, a2) -> a2.getHotNumber().compareTo(a1.getHotNumber())).collect(Collectors.toList());
        return articles.size() >= size ? articles.subList(0, size) : articles;
    }

    @Override
    public List<ArticleDTO> findNewArticlesWithUser(int size) {

        ArticleQueryParam articleQueryParam = new ArticleQueryParam();
        articleQueryParam.setOrder("create_time");
        articleQueryParam.setSort("desc");

        List<Article> articles = articleMapper.selectAllByQueryParamPage(new Page<>(0, size), articleQueryParam);

        List<ArticleDTO> articleDTOS = new LinkedList<>();
        articles.forEach(article -> {
            try {
                articleDTOS.add(changeEntityToDTO(article));
            } catch (InvocationTargetException | IllegalAccessException e) {
                throw new ModelCoverDTOFailedException();
            }
        });

        return articleDTOS;
    }

    @Override
    public ArticleDTO findWithUserByArticleId(long id) {

        Article article = articleMapper.findWithUserByArticleId(id);

        if (article != null) {
            try {
                return changeEntityToDTO(article);
            } catch (InvocationTargetException | IllegalAccessException e) {
                throw new ModelCoverDTOFailedException();
            }
        } else {
            return null;
        }

    }

    @Override
    public List<Map> getArticleArchives() {
        return articleMapper.getArticleArchives();
    }

    @Override
    public int saveByForm(Article article) {

        if (article.getFavoriteNumber() == null) {
            article.setFavoriteNumber(0);
        }
        if (article.getLikeNumber() == null) {
            article.setLikeNumber(0);
        }
        if (article.getReadNumber() == null) {
            article.setReadNumber(0);
        }
        if (article.getCreateTime() == null) {
            article.setCreateTime((int) (System.currentTimeMillis() / 1000));
        }
        if (article.getTags() != null) {
            article.setTags(tagsService.findAndInsertIdsWithNames(article.getTags()));
        }

        return articleMapper.insert(article);
    }

    @Override
    public boolean edit(Long id, Article article) {

        Article oldArticle = articleMapper.selectById(id);

        if (article != null) {

            try {
                CombineBeansUtil.combineBean(article, oldArticle);
            } catch (InvocationTargetException | IllegalAccessException e) {
                throw new ModelCoverDTOFailedException();
            }

            if (oldArticle.getTags() != null) {
                oldArticle.setTags(tagsService.findAndInsertIdsWithNames(oldArticle.getTags()));
            }

            return articleMapper.updateById(oldArticle) > 0;
        } else {
            throw new ModelNotFoundException();
        }
    }

    @Override
    public boolean readNumInc(Long id) {

        Article article = articleMapper.selectById(id);

        if (article != null) {
            article.setReadNumber(article.getReadNumber() + 1);
            return articleMapper.updateById(article) > 0;
        } else {
            throw new ModelNotFoundException();
        }
    }

    @Override
    public void delete(long id) {
        articleMapper.deleteById(id);
    }

    @Override
    public IPage<Article> findAll(Integer page, Integer pageSize) {

        QueryWrapper<Article> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderBy(true, false, "article_id");

        return articleMapper.selectPage(new Page<>(page, pageSize), queryWrapper);
    }

    @Override
    public IPage<ArticleDTO> findAll(Integer page, Integer pageSize, ArticleQueryParam articleQueryParam) {

        if (articleQueryParam.getOrder() == null) {
            articleQueryParam.setOrder("article_id");
        }

        if (articleQueryParam.getSort() == null) {
            articleQueryParam.setSort("desc");
        }

        IPage<ArticleDTO> iPage = new Page<>(page, pageSize);
        List<Article> articleList = articleMapper.selectAllByQueryParamPage(iPage, articleQueryParam);

        List<ArticleDTO> result = coverToDTOList(articleList);

        iPage.setRecords(result);
        return iPage;
    }


    @Override
    public int getHotNumber(Article article) {
        //热门算法的计算规则是 ((收藏数 * 2 + 点赞数 * 2 + 阅读数)  / 到现在为止发布的小时数 + 1) * 文章重要度
        int hour = (((int) (System.currentTimeMillis() / 1000)) - article.getCreateTime()) / 3600;
        return (((article.getFavoriteNumber() * 5) + (article.getLikeNumber() * 2) + article.getReadNumber()) / (hour + 1));
    }

    @Override
    public int countByCategoryId(Long categoryId) {

        QueryWrapper<Article> qw = new QueryWrapper<>();
        qw.eq("category_id", categoryId);

        return articleMapper.selectCount(qw);
    }

    private List<ArticleDTO> coverToDTOList(List<Article> articleList) {
        List<ArticleDTO> result = new LinkedList<>();
        articleList.forEach(article -> {
            try {
                ArticleDTO articleDTO = changeEntityToDTO(article);
                result.add(articleDTO);
            } catch (InvocationTargetException | IllegalAccessException e) {
                throw new ModelCoverDTOFailedException();
            }
        });
        return result;
    }

    /**
     * 将Article Entity 转化为 ArticleDTO
     * 同时设置点赞数 文章热度等
     *
     * @param article 文章实体类
     */
    private ArticleDTO changeEntityToDTO(Article article) throws InvocationTargetException, IllegalAccessException {
        ArticleDTO articleDTO = new ArticleDTO();
        CombineBeansUtil.combineBean(article, articleDTO);
        if (article.getUser() != null) {
            articleDTO.setUserId(article.getUserId());
            articleDTO.setNickName(article.getUser().getNickName());
        }
        if (article.getCategory() != null) {
            articleDTO.setCategoryName(article.getCategory().getTitle());
        }
        if (article.getTags() != null) {
            articleDTO.setTags(tagsService.findTagsNamesOfIds(article.getTags()));
        }

        //计算文章热度
        articleDTO.setHotNumber(this.getHotNumber(article));
        //文章评论数
        articleDTO.setCommentNumber(commentService.countByArticleId(article.getArticleId()));

        return articleDTO;
    }

}
